package com.chujian.system.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.chujian.framework.bean.IdName;
import com.chujian.framework.util.TreeUs;
import com.chujian.sys.entity.SysMenu;
import com.chujian.sys.entity.SysRoleMenu;
import com.chujian.sys.vo.menu.Menu;
import com.chujian.sys.vo.menu.MenuMeta;
import com.chujian.system.sys.service.SysMenuService;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class RoleMenuCombiner {

    private final Long menuTemplateId;
    private final List<SysMenu> allMenus;
    private final List<SysRoleMenu> roleMenus;

    // 顶级菜单
    private List<Menu> topMenus;
    // 菜单映射 MenuId -> Menu
    private final Map<Long, Menu> menuMap = new HashMap<>();
    // 菜单列表 Menu
    private final List<Menu> menus = new ArrayList<>();
    // 功能ID -> 角色list
    private LinkedHashMap<Long, List<SysRoleMenu>> menuRoles;

    /**
     * @param menuTemplateId 菜单模板ID
     * @param allMenus       所有菜单
     * @param roleMenus      角色菜单
     */
    public RoleMenuCombiner(Long menuTemplateId, List<SysMenu> allMenus, List<SysRoleMenu> roleMenus) {
        this.menuTemplateId = menuTemplateId;
        this.allMenus = allMenus;
        this.roleMenus = roleMenus;
    }

    public List<Menu> combine() {
        // 收集菜单
        collMenus();
        // 转换为树
        toTree();
        // 标记选中
        markChecked();
        // 删除未被选中的节点
        removeUnCheckedNode(topMenus);
        // ++ 清除中间数据
        removeProcessData();
        return topMenus;
    }

    private void collMenus() {
        // 收集权限
        menuRoles = roleMenus.stream()
                .collect(Collectors.groupingBy(SysRoleMenu::getMenuId, LinkedHashMap::new, Collectors.toList()));

        // 收集菜单
        for (SysMenu sysMenu : allMenus) {
            Menu menu = convertMenu(sysMenu);
            menuMap.put(menu.getId(), menu);
            menus.add(menu);
        }

        // 设置路径
        for (Menu menu : menus) {
            menu.setPaths(findMenuPath(menu.getId()));
        }
    }

    private void toTree() {
        topMenus = TreeUs.toTree(menus, SysMenuService.PARENT_MENU_ID, new TreeUs.ITreeCallback<Menu>() {
            @Override
            public Serializable getId(Menu menu) {
                return menu.getId();
            }

            @Override
            public Serializable getPId(Menu menu) {
                return menu.getPid();
            }

            @Override
            public List<Menu> getChildren(Menu menu) {
                return menu.getChildren();
            }

            @Override
            public void afterAddChild(Menu parent, Menu item) {
                // 设置菜单的redirect
                if (parent.getChildren().size() == 1) {
                    parent.setRedirect(parent.getPath() + "/" + item.getPath());
                }
            }
        });
    }

    private void markChecked() {
        // 授权IDs
        Set<Long> roledMenuIds = roleMenus.stream()
                .map(SysRoleMenu::getMenuId)
                .collect(Collectors.toSet());

        menus.stream()
                .filter(e -> roledMenuIds.contains(e.getId()))
                .map(Menu::getPaths)
                // 拉平
                .flatMap(Collection::stream)
                .peek(roleId -> menuMap.get(roleId).setChecked(true))
                .collect(Collectors.toList());
    }

    private void removeUnCheckedNode(List<Menu> menus) {
        if (CollUtil.isEmpty(menus)) return;

        int[] removeIds = CollUtil.indexOfAll(menus, m -> !m.getChecked());

        // 执行删除
        for (int i = removeIds.length - 1; i >= 0; i--) {
            menus.remove(removeIds[i]);
        }

        // 递归删除子节点
        for (Menu menu : menus) {
            removeUnCheckedNode(menu.getChildren());
        }
    }

    private void removeProcessData() {

    }

    // 获取节点路径
    private List<Long> findMenuPath(Long fromMenuId) {
        Menu menu = menuMap.get(fromMenuId);
        List<Long> paths = new ArrayList<>();
        do {
            paths.add(menu.getId());
            // 向上查找
            menu = menuMap.get(menu.getPid());
        } while (null != menu);
        return CollUtil.reverse(paths);
    }

    private Menu convertMenu(SysMenu sysMenu) {
        List<SysRoleMenu> roleMenus = menuRoles.get(sysMenu.getMenuId());

        boolean hasAuth = CollUtil.isNotEmpty(roleMenus);
        List<IdName> myRoles = !hasAuth ? new ArrayList<>()
                : roleMenus.stream()
                .map(e -> new IdName(e.getRoleId(), e.getRoleName()))
                .collect(Collectors.toList());

        return new Menu()
                .setId(sysMenu.getId())
                .setPid(sysMenu.getMenuPid())
                .setPath(sysMenu.getPath())
                .setRedirect("")
                .setComponent(sysMenu.getComponentUrl())
                .setName(sysMenu.getComponentName())
                .setExpJson(sysMenu.getExpJson())
                .setOpenType(sysMenu.getOpenType())
                .setMeta(
                        new MenuMeta()
                                .setTitle(sysMenu.getMenuName())
                                .setIcon(sysMenu.getIcon())
                                .setIgnoreKeepAlive(!sysMenu.getIsCache())
                                .setHideMenu(!sysMenu.getIsVisible())
                                .setFuncId(sysMenu.getId())
                                .setFuncMark(sysMenu.getComponentName())
                                .setMyRoles(myRoles)
                )
                .setChildren(new ArrayList<>())
                .setChecked(hasAuth)
                ;
    }

}
